package cn.wolfcode.wolf2w.service.impl;

import cn.wolfcode.wolf2w.domain.UserInfo;
import cn.wolfcode.wolf2w.exception.LogicException;
import cn.wolfcode.wolf2w.redis.service.IUserInfoRedisService;
import cn.wolfcode.wolf2w.redis.util.RedisKeys;
import cn.wolfcode.wolf2w.repository.UserInfoRepository;
import cn.wolfcode.wolf2w.service.IUserInfoService;
import cn.wolfcode.wolf2w.util.AssertUtils;
import cn.wolfcode.wolf2w.util.Consts;
import com.alibaba.fastjson.JSON;
import org.apache.catalina.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.TimeoutUtils;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Service
public class UserInfoServiceImpl implements IUserInfoService {
    @Autowired
    private UserInfoRepository userInfoRepoository;

    @Autowired
    private StringRedisTemplate redisTemplate;
    //核心项目pom.xml有依赖了,我这里可以使用


    @Value("${sms.url}")
    private String smsUrl;//获取ap配置文件的数据
    @Value("${sms.appkey}")
    private String smsAppkey;



    BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();

    @Override
    public void save(UserInfo u) {
        u.setId(null);
        userInfoRepoository.save(u);
    }

    @Override
    public void update(UserInfo u) {
        userInfoRepoository.save(u);
    }

    @Override
    public UserInfo get(String id) {
//null做为参数,为null,就返回空;不为空就返回对象
// return value != null ? value : other;
        return userInfoRepoository.findById(id).orElse(null);
    }

    @Override
    public List<UserInfo> listAll() {
        return userInfoRepoository.findAll();
    }

    @Override
    public void delete(String id) {
        userInfoRepoository.deleteById(id);
    }

    @Override
    public boolean findByPhone(String phone) {
        //查询查询数据库有没有同名的user对象
        UserInfo userInfo = userInfoRepoository.findByPhone(phone);
//        userInfo对象有值,说明是已经被人注册了;userInfo对象无值,返回false表示没有没人注册过,可以注册
        //        userInfo对象有值,返回true;无值,返回false
        return userInfo != null;//
    }

    //获取Redis的验证码
    @Override
    public String getVerifyCode(String phone) {//用户传来的验证码和Redis的验证码对比,相同就让他注册

      //  String code = "verify_code:" + phone;
//调用枚举类的方法,和电话号码拼接成key,获取Redis数据库的对应的value值
        String key = RedisKeys.VERIFY_CODE.join(phone);
        //返回Redis的验证码
        return redisTemplate.opsForValue().get(key);
    }

    //把验证码存入Redis
    @Override
    public void sendVerifyCode(String phone) {
        System.out.println(phone);
        //1:创建短信验证码
        //生成随机数去掉"-"号,取前四位做为验证码
        String code = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 4);
       // String key = "verify_code:" + phone;//Redis要求key保证是唯一的,刚好phone手机号是唯一,再加上前缀好区分
        //调用枚举类获取拼接好的key  用来存在Redis里面
        String key = RedisKeys.VERIFY_CODE.join(phone);

        //2:拼接一个短信
        StringBuilder sb = new StringBuilder(80);
        sb.append("您好, 您的注册验证码是:").append(code).append("请在").append(Consts.VERIFY_CODE_VAI_TIME)
                .append("分钟之内使用");
        //3:发送短信 : 假装发一个短信
//        Consts.VERIFY_CODE_VAI_TIME*60  300秒

        //TimeUnit.SECONDS数据在数据库的存活时单位是秒  60L表示秒(long类型),Timenit.SECONDS是单位秒
        redisTemplate.opsForValue().set(key, code, 60L, TimeUnit.SECONDS);
        String s = redisTemplate.opsForValue().get(key);//根据key获取储存在Redis缓存库的验证码
        System.out.println(sb.toString());

//        真实的注释掉
        //4.真实的短信发送: 需要借助短信网关的api: 发起http请求
//        RestTemplate restTemplate = new RestTemplate();
//        String ret = restTemplate.getForObject(smsUrl, String.class, phone, "【巴卜技术】" + sb.toString(), smsAppkey);
////        Url:https://way.jd.com/BABO/sms?mobile=15000000000&msg=【巴卜技术】您的验证码是201422,若非本人操作请忽略&appkey=6fedb0cfed7644d31d7df85621040060
//        System.out.println(ret);
//        if (!ret.contains("10000")) {//false  //包含Success或者状态码是10000是成功
//            throw new LogicException("验证码发送失败");
//        }
        System.err.println(s);
    }
//非代码(网关返回值)
//    "code": "10000",
//    "charge": false,
//    "remain": 0,
//    "msg": "查询成功",
//    "result": {
//        "resptime": 20200715011658,
//        "respstatus": 116,
//        "msgid": -100
//    }


    //注册
    @Override
    public void regist(String phone, String nickname, String password,
                       String rpassword, String verifyCode) {
 //1:参数是否为空
        AssertUtils.hasLength(phone, "手机号不能为空");
        AssertUtils.hasLength(nickname, "昵称不能为空");
        AssertUtils.hasLength(password, "密码不能为空");
        AssertUtils.hasLength(rpassword, "确认密码不能为空");
        AssertUtils.hasLength(verifyCode, "短信验证码不能为空");
        //2:两次输入密码是否一致
        AssertUtils.isEqual(rpassword, password, "两次输入密码不一致");
        //模拟短信验证码发送
        //5:短信验证码是否正确
//判断Redis的验证码和用户传来的验证码是不是一样,吧是一样就抛异常
//        用户传的验证码有值,继续执行
        //获取Redis验证码
        String code = this.getVerifyCode(phone);



        System.out.println(code);
        if (code == null || !code.equalsIgnoreCase(verifyCode)) {
            throw new LogicException("验证码输入错误");
        }


        AssertUtils.isEqual(verifyCode, code, "验证码错误");
        //6:用户注册
        UserInfo u = new UserInfo();
        //把用户在注册填写的部分信息,没有填写的是空字符串.保证mongo数据库的表
        // 有这个属性
        u.setId("");
        u.setNickname(nickname);
        u.setPhone(phone);
        u.setCity("");
//        u.setPassword(bCryptPasswordEncoder.encode(password));
        u.setPassword(password);
        u.setEmail("");
        u.setLevel(0);
        u.setGender(UserInfo.GENDER_SECRET);
        u.setHeadImgUrl("/images/default.png");
        u.setInfo("");

//
//关键字段自己控制:原因是在公司别人会把
//你的代码改掉.不使用默认值.
        u.setState(UserInfo.STATE_NORMAL);//状态:正常
        //把用户在注册时填写的部分信息储存在user对象后,调用新增方法插入数据库中

        this.save(u);
//        统一异常测试:模拟
//        throw new RuntimeException("tuetuxttfkuoi");

    }

    @Override
    public UserInfo login(String username, String password) {
        //根据用户名(手机号)查询用户对象
        UserInfo user = userInfoRepoository.findByPhone(username);
        //密码不相等
        if(user==null||!user.getPassword().equals(password)){
            //如果根据名字查询不到用户对象,或者密码不相同,就说明用户没有登录,
            // 就抛个异常提示用户登录(统一异常 捕获抛的异常后,
            // 返回jsonResult对象给前端,前端获取出来设在页面显示提示用户)
            throw new LogicException("密码或者用户名错误");
        }
        return user;//如果对象有值,或者密码相同就返回用户对象,继续执行程序
    }

    @Override
    public String setToken(UserInfo user) {
        //生成随机数,用来作为token
        String token = UUID.randomUUID().toString().replaceAll("-", "");
        //token做为key,和指定的这个user对象一起存入Redis数据库
        //JSON.toJSONString(user) 把用户转成字符串再作为value值存入redis数据库
        //key,value,存活时间,单位
        redisTemplate.opsForValue().set(token, JSON.toJSONString(user),RedisKeys.VERIFY_CODE.getTime(),TimeUnit.SECONDS);
        return token;
    }
//重置时间
    //根据token获取用户对象
    //每次请求经过拦截器时,调用这个方法,设置了每次访问后,
// 就延长数据存储在Redis的时间(不会判断用户对象为空,不会被拦截住,不给访问)
    @Override
    public UserInfo getUserByToken(String token) {
        if (!StringUtils.hasLength(token)) {
            return null;
        }
        //如果Redis的key有值,才获取用户对象
        if (redisTemplate.hasKey(token)) {
            String user = redisTemplate.opsForValue().get(token);
            UserInfo userInfo = JSON.parseObject(user, UserInfo.class);
//            /重置时间:30分钟,     TimeUnit.SECONDS单位是秒
            redisTemplate.expire(token,RedisKeys.VERIFY_CODE.getTime(),TimeUnit.SECONDS);
            return  userInfo;
        }
        return null;
    }
}
